package com.gksc.video.manager;

import com.gksc.video.config.PushConfig;
import com.gksc.video.constant.BusinessConstant;
import com.gksc.video.constant.CacheConstant;
import com.gksc.video.data.DeviceInfo;
import com.gksc.video.data.OpResInfo;
import com.gksc.video.device.DHDevicePlay;
import com.gksc.video.device.HCDevicePlay;
import com.gksc.video.enums.DeviceFirm;
import com.gksc.video.enums.StreamType;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;

import java.io.PipedInputStream;
import java.io.PipedOutputStream;

/**
 * @className: DevicePushThread
 * @description: 设备推视频流线程
 * @author: Liao.yx
 * @email: 1229053515@qq.com
 * @create: 2021年05月19日 15时31分
 * @copyRight: 2020 liaoyongxiang All rights reserved.
 **/
@Slf4j
public class DevicePushThread extends BasePushThread implements Runnable {
    /**
     * 录像设备信息
     */
    private DeviceInfo cameraInfo;
    /**
     * 采集器
     */
    private FFmpegFrameGrabber grabber;
    /**
     * 视频输出流
     */
    private PipedOutputStream outStream;
    /**
     * 视频输入流
     */
    private PipedInputStream inStream;
    /**
     * 海康设备播放取流器
     */
    private HCDevicePlay hcPlay;
    /**
     * 大华设备播放取流器
     */
    private DHDevicePlay dhPlay;
    /**
     * 推流配置信息
     */
    private PushConfig pushConfig;

    public DevicePushThread(DeviceInfo cameraInfo, PushConfig pushConfig) {
        this.cameraInfo = cameraInfo;
        this.pushConfig = pushConfig;
    }

    @Override
    public void run() {
        try {
            CacheConstant.STREATMAP.put(cameraInfo.getToken(), cameraInfo);
            CacheConstant.JOBMAP.put(cameraInfo.getToken(), this);
            Integer probeSize = StreamType.MAIN.getType() == cameraInfo.getStream() ? pushConfig.getMainCode() : pushConfig.getSubCode();
            if (DeviceFirm.Hik.getCode().equals(cameraInfo.getDevFirm())) {
                outStream = new PipedOutputStream();
                hcPlay = new HCDevicePlay(outStream, cameraInfo.getBitRate());
                OpResInfo<Integer> res = hcPlay.play(cameraInfo);
                if (!res.isOpRes()) {
                    log.error("海康设备{}回放失败，错误码是{}", cameraInfo, res.getErrorDesc());
                }
            } else if (DeviceFirm.Dahua.getCode().equals(cameraInfo.getDevFirm())) {
                outStream = new PipedOutputStream();
                dhPlay = new DHDevicePlay(outStream, probeSize);
                OpResInfo<Integer> res = dhPlay.play(cameraInfo);
                if (!res.isOpRes()) {
                    log.error("大华设备{}回放失败，错误码是{}", cameraInfo, res.getErrorDesc());
                }
            } else {
                log.error("{}未知设备厂商，无法进行下一步操作", cameraInfo);
                return;
            }
            inStream = new PipedInputStream();
            inStream.connect(outStream);
            grabber = new FFmpegFrameGrabber(inStream, 0);
            grabber.setOption(BusinessConstant.GrabberOption.BUFFER_SIZE, "1024000");
            grabber.setOption(BusinessConstant.GrabberOption.MAX_DELAY, "500000");
            grabber.setOption(BusinessConstant.GrabberOption.TIMEOUT, "20000000");
            //避免grabber.start();发生阻塞
            long start = System.currentTimeMillis();
            while (true) {
                Thread.sleep(100);
                long end = System.currentTimeMillis();
                if (end - start > 2000) {
                    log.error("设备{}发生阻塞", cameraInfo.toString());
                    return;
                }
                if (inStream.available() == 1024) {
                    break;
                }
            }
            grabber.start();
            super.push(grabber, cameraInfo);
        } catch (Exception e) {
            log.error("推流失败，设备信息：{}，异常原因{}", cameraInfo.toString(), e);
            e.printStackTrace();
        } finally {
            release();
            CacheConstant.STREATMAP.remove(cameraInfo.getToken());
            CacheConstant.JOBMAP.remove(cameraInfo.getToken());
            log.info("结束推流 设备信息：{}", cameraInfo.toString());
        }
    }

    /**
     * @return void
     * @Title: release
     * @Description:资源释放
     **/
    public void release() {
        log.info("{}释放资源", Thread.currentThread().getName());
        try {
            if (hcPlay != null) {
                hcPlay.stop();
            }
            if (dhPlay != null) {
                dhPlay.stop();
            }
            if (grabber != null) {
                grabber.stop();
                grabber.close();
            }
            super.release();
            if (inStream != null) {
                inStream.close();
            }
            if (outStream != null) {
                outStream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 线程终止退出
     */
    public void cancel() {
        super.cancel();
    }
}
